package messages

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"regexp"
	"sort"
	"strings"
)

type Service interface {
	MessageSearch(ctx context.Context, searchMessageOpt messageSearchRequest) (count int64, d [][]Message, haveNext bool, headData Message, tailData Message, err error)
	MessageContext(ctx context.Context, contextOpt messageContextRequest) (d []Message, err error)
	AlarmMessage(ctx context.Context, topicIsDefault bool, alarmMessageOpt alarmMessageRequest) (count int64, d [][]Message, haveNext bool, headData Message, tailData Message, err error)
	HighlightWords(topics []Topic, text string) (highlight string, err error)
	SearchFile(ctx context.Context, searchFileOpt searchFileRequest) (count int64, d []File, err error)
	SetTag(ctx context.Context, setTagOpt setTagRequest) (d []tagInfo, err error)
	FileTravel(ctx context.Context, id string) ([]Message, error)
}

type service struct {
	messageRepository MessageRepository
	fileRepository    FileRepository
	fileDir           string
	logger            log.Logger
}

func NewService(rep MessageRepository, fr FileRepository, logger log.Logger, fileDir string) Service {
	return &service{
		messageRepository: rep,
		fileRepository:    fr,
		logger:            logger,
		fileDir:           fileDir,
	}
}

func (svc *service) SearchFile(ctx context.Context, searchFileOpt searchFileRequest) (int64, []File, error) {
	count, files, err := svc.fileRepository.SearchFile(ctx, searchFileOpt, svc.fileDir)
	if err != nil {
		return 0, nil, err
	}
	var newFiles []File
	for _, f := range files {
		newFiles = append(newFiles, *f)
	}
	return count, newFiles, nil
}

func (svc *service) SetTag(ctx context.Context, setTagOpt setTagRequest) (d []tagInfo, err error) {
	data, err := svc.fileRepository.SetTag(ctx, setTagOpt.Id, setTagOpt.Tags)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func (svc *service) FileTravel(ctx context.Context, id string) ([]Message, error) {
	var messages []Message
	hits, err := svc.fileRepository.Travel(ctx, id)
	if err != nil {
		_ = level.Error(svc.logger).Log("es error:", err)
		return nil, err
	}
	if len(hits) > 0 {
		for _, hit := range hits {
			var message Message
			err := json.Unmarshal(hit.Source, &message)
			if err != nil {
				_ = level.Error(svc.logger).Log("json error:", err)
				return nil, err
			}
			messages = append(messages, message)
		}
	}
	return messages, nil
}

// 预警消息
func (svc *service) AlarmMessage(ctx context.Context, topicIsDefault bool, alarmMessageOpt alarmMessageRequest) (count int64, d [][]Message, haveNext bool, headData Message, tailData Message, err error) {
	var size int64
	var skip int64
	var lastData Message

	if len(alarmMessageOpt.Data.Id) > 0 {
		lastData = alarmMessageOpt.Data
		skip = alarmMessageOpt.Skip
	}
	size = alarmMessageOpt.Size
	if size == 0 {
		size = 20
	}

	if skip >= 0 {
		for skip >= 0 {
			// 向后翻页
			count, d, lastData, haveNext, err = svc.AlarmMessageFolding(ctx, topicIsDefault, alarmMessageOpt, lastData)
			if err != nil {
				return 0, nil, false, headData, tailData, err
			}
			if len(d) == 0 || len(d[0]) == 0 {
				return count, d, haveNext, headData, tailData, err
			}
			headData = d[0][0]
			tailData = lastData
			skip -= size
		}
	} else {
		// 向前翻
		for skip < -size {
			count, d, headData, haveNext, err = svc.AlarmMessageFolding(ctx, topicIsDefault, alarmMessageOpt, lastData)
			if err != nil {
				return 0, nil, false, headData, tailData, err
			}
			if len(d) == 0 || len(d[0]) == 0 {
				return count, d, haveNext, headData, tailData, err
			}
			tailData = d[0][0]
			for k := range d {
				sort.Slice(d[k], func(i, j int) bool {
					return d[k][i].Index > d[k][j].Index
				})
			}
			sort.Slice(d, func(i, j int) bool {
				return d[i][0].Index > d[j][0].Index
			})
			skip += size
			if len(d) == 0 || len(d[0]) == 0 {
				return count, d, haveNext, headData, tailData, err
			}
			lastData = d[0][0]
		}
	}
	return count, d, haveNext, headData, tailData, err
}

// 预警消息折叠
func (svc *service) AlarmMessageFolding(ctx context.Context, topicIsDefault bool, searchMessageOpt alarmMessageRequest, tailData Message) (int64, [][]Message, Message, bool, error) {
	var (
		size     int64
		haveNext = false
		messages [][]Message
	)
	if len(searchMessageOpt.Data.Id) == 0 {
		searchMessageOpt.Skip = 0
	}

	size = searchMessageOpt.Size
	if size == 0 {
		size = 20
	}
	var messageIndex int

	repeatMap := make(map[string]int)
	for {
		totalHits, hits, err := svc.messageRepository.AlarmMessage(ctx, topicIsDefault, searchMessageOpt, size*3, tailData)
		if err != nil {
			return 0, nil, tailData, false, err
		}
		if len(hits) > 0 {
			for index, hit := range hits {
				var message Message
				err := json.Unmarshal(hit.Source, &message)
				if err != nil {
					_ = level.Error(svc.logger).Log("json error:", err)
					return 0, nil, tailData, false, err
				}
				message.Index = messageIndex
				messageIndex++
				if hit.Score != nil {
					message.Score = *hit.Score
				}
				targetTopics := svc.TargetTopics(topicIsDefault, message.Topics, searchMessageOpt)
				message.StringTopics = svc.StringTopics(targetTopics)
				highlight, err := svc.HighlightWords(targetTopics, message.Content)
				message.Topics = targetTopics
				if err != nil {
					_ = level.Error(svc.logger).Log("highlight error:", err)
				}
				message.Highlight = highlight
				if idx, ok := repeatMap[message.Content]; ok {
					messages[idx] = append(messages[idx], message)
				} else {
					if len(messages) == int(size) {
						if index < len(hits)-1 {
							haveNext = true
						} else {
							_, oneHits, _ := svc.messageRepository.AlarmMessage(ctx, topicIsDefault, searchMessageOpt, 1, tailData)
							if len(oneHits) > 0 {
								haveNext = true
							}
						}
						return totalHits, messages, tailData, haveNext, err
					}
					repeatMap[message.Content] = len(messages)
					messages = append(messages, []Message{message})
				}
				tailData = message
			}
		} else {
			return totalHits, messages, tailData, haveNext, err
		}
	}
}

// 高亮关键词
func (svc *service) HighlightWords(topics []Topic, text string) (highlight string, err error) {
	for _, topic := range topics {
		for _, keyword := range topic.Keywords {
			if exists := strings.Contains(text, keyword); !exists {
				continue
			}
			pattern, err := regexp.Compile(keyword)
			if err != nil {
				return "", err
			}
			var reply string
			if topic.IsDefault == true {
				reply = fmt.Sprintf(`<span style= "color:#FF0909;font-weight: bolder;">%s</span>`, keyword)
			} else {
				reply = fmt.Sprintf(`<span style= "color:#E78D08;font-weight: bolder;">%s</span>`, keyword)
			}
			text = pattern.ReplaceAllString(text, reply)
		}
	}
	return text, nil
}

func (svc *service) StringTopics(topics []Topic) []string {
	var stringTopics []string
	for _, topic := range topics {
		stringTopics = append(stringTopics, topic.Name)
	}
	return stringTopics
}

func (svc *service) TargetTopics(topicIsDefault bool, topics []Topic, opt alarmMessageRequest) []Topic {
	var targetTopics []Topic
	if len(opt.Topic) > 0 {
		for _, topic := range topics {
			if opt.Topic == topic.Id {
				targetTopics = append(targetTopics, topic)
			}
		}
	} else {
		for _, topic := range topics {
			if topic.IsDefault == topicIsDefault {
				targetTopics = append(targetTopics, topic)
			}
		}
	}
	return targetTopics
}

// 消息上下文
func (svc *service) MessageContext(ctx context.Context, contextOpt messageContextRequest) ([]Message, error) {
	var (
		before, after             int64 = 0, 0
		beforeResult, afterResult []Message
	)

	before = contextOpt.Before
	after = contextOpt.After
	data := contextOpt.Data
	if before > 0 {
		hits, err := svc.messageRepository.SearchContext(ctx, false, before, data)
		if err != nil {
			return nil, err
		}
		for _, hit := range hits {
			var message Message
			err := json.Unmarshal(hit.Source, &message)
			if err != nil {
				_ = level.Error(svc.logger).Log("json error:", err)
				return nil, err
			}
			beforeResult = append(beforeResult, message)
		}
		ReverseMessage(beforeResult)
	}
	if after > 0 {
		hits, err := svc.messageRepository.SearchContext(ctx, true, after, data)
		if err != nil {
			return nil, err
		}
		for _, hit := range hits {
			var message Message
			err := json.Unmarshal(hit.Source, &message)
			if err != nil {
				_ = level.Error(svc.logger).Log("json error:", err)
				return nil, err
			}
			afterResult = append(afterResult, message)
		}
	}
	if after > 0 && before > 0 {
		beforeResult = append(beforeResult, data)
		beforeResult = append(beforeResult, afterResult...)
		return beforeResult, nil
	} else if after > 0 {
		return afterResult, nil
	} else {
		return beforeResult, nil
	}
}

// 消息检索
func (svc *service) MessageSearch(ctx context.Context, searchMessageOpt messageSearchRequest) (count int64, d [][]Message, haveNext bool, headData Message, tailData Message, err error) {
	var size int64
	var skip int64
	var lastData Message

	if len(searchMessageOpt.Data.Id) > 0 {
		lastData = searchMessageOpt.Data
	}
	if len(searchMessageOpt.Data.Id) > 0 {
		skip = searchMessageOpt.Skip
	}
	size = searchMessageOpt.Size
	if size == 0 {
		size = 20
	}

	if len(searchMessageOpt.CategoryId) > 0 {
		ids, err := svc.messageRepository.GetChatroomIdsByCategoryId(ctx, searchMessageOpt.CategoryId)
		if err != nil {
			return 0, nil, false, Message{}, Message{}, err
		}
		searchMessageOpt.ChatroomIds = ids
	}

	if skip >= 0 {
		// 向后翻页
		for skip >= 0 {
			count, d, lastData, haveNext, err = svc.MessageFolding(ctx, searchMessageOpt, lastData)
			if err != nil {
				return 0, nil, false, headData, tailData, err
			}
			if len(d) == 0 || len(d[0]) == 0 {
				return count, d, haveNext, headData, tailData, err
			}
			headData = d[0][0]
			tailData = lastData
			skip -= size
		}
	} else {
		for skip < -size {
			// 向前翻页
			count, d, headData, haveNext, err = svc.MessageFolding(ctx, searchMessageOpt, lastData)
			if err != nil {
				return 0, nil, false, headData, tailData, err
			}
			if len(d) == 0 || len(d[0]) == 0 {
				return count, d, haveNext, headData, tailData, err
			}
			tailData = d[0][0]
			for k := range d {
				sort.Slice(d[k], func(i, j int) bool {
					return d[k][i].Index > d[k][j].Index
				})
			}
			sort.Slice(d, func(i, j int) bool {
				return d[i][0].Index > d[j][0].Index
			})
			skip += size
			if len(d) == 0 || len(d[0]) == 0 {
				return count, d, haveNext, headData, tailData, err
			}
			lastData = d[0][0]
		}
	}
	return count, d, haveNext, headData, tailData, err
}

// 消息折叠
func (svc *service) MessageFolding(ctx context.Context, searchMessageOpt messageSearchRequest, tailData Message) (int64, [][]Message, Message, bool, error) {
	var (
		size     int64
		haveNext = false
		messages [][]Message
	)

	size = searchMessageOpt.Size
	if size == 0 {
		size = 20
	}

	var messageIndex int
	repeatMap := make(map[string]int)
	// 搜索size*2数量的消息
	// 判断是否重复，然后折叠
	// 如果数据量小于size，继续查询
	for {

		totalHits, hits, err := svc.messageRepository.SearchMessage(ctx, searchMessageOpt, size*3, tailData)
		if err != nil {
			return 0, nil, tailData, false, err
		}
		if len(hits) > 0 {
			for index, hit := range hits {
				var message Message
				err := json.Unmarshal(hit.Source, &message)
				if err != nil {
					_ = level.Error(svc.logger).Log("json error:", err)
					return 0, nil, tailData, false, err
				}
				message.Index = messageIndex
				messageIndex++
				if hit.Score != nil {
					message.Score = *hit.Score
				}
				if idx, ok := repeatMap[message.Content]; ok {
					messages[idx] = append(messages[idx], message)
				} else {
					if len(messages) == int(size) {
						if index < len(hits)-1 {
							haveNext = true
						} else {
							_, oneHits, _ := svc.messageRepository.SearchMessage(ctx, searchMessageOpt, 1, tailData)
							if len(oneHits) > 0 {
								haveNext = true
							}
						}
						return totalHits, messages, tailData, haveNext, err
					}
					repeatMap[message.Content] = len(messages)
					messages = append(messages, []Message{message})
				}
				tailData = message
			}
		} else {
			return totalHits, messages, tailData, haveNext, err
		}
	}
}

// 逆序[]切片
func ReverseSlice(s [][]Message) [][]Message {
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}

// 逆序Message切片
func ReverseMessage(s []Message) []Message {
	for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
		s[i], s[j] = s[j], s[i]
	}
	return s
}
